//
//  CCBLoader.h
//  LeftRight
//
//  Created by 杨岑 on 7/11/14.
//
//

#ifndef _CCBI_LOADER_H_
#define _CCBI_LOADER_H_

#include <unordered_map>
using namespace std;

#include "cocos2d.h"
#include "cocos-ext.h"
#include "CocosBuilder/CocosBuilder.h"
using namespace cocos2d;
using namespace cocos2d::extension;
using namespace cocosbuilder;

/****
    Typical usage of this class:

    // Assume you already declared varName/valueName as class member variables.
    void YourClass::SomeMethod()
    {
        const char* ccbiFile = "dir/ccbifile"; // .ccbi file extention is optional
        BEGIN_CCBI_LOADER(ccbiFile)
        CCBI_MEMBER(varName)
        CCBI_PROPERTY(valueName)
        CCBI_CONTROL_HANDLER("testButtonClick", TestLayer::testButtonClick)
        END_CCBI_LOADER
 
        // However, you could use local variables in theory.
 
        // At this point, all variables above between BEGIN_ and END_ macros have been populated.
 
        // Add ccbiLoaderNode which is declared by END_CCBI_LOADER macro to children collection.
        this->addChild(ccbiLoaderNode);
    }
 
    BEGIN_CCBI_LOADER uses 'this' as CCBReader owner. You can use BEGIN_CCBI_LOADER2 to specify
    other owner if 'this' is not suitable. There are also '2' versions for CCBI_MEMBER, CCBI_PROPERTY.
*/

class CCBILoader:
    public Ref,
    public CCBSelectorResolver,
    public CCBMemberVariableAssigner,
    public NodeLoaderListener
{
public:
    typedef Node* NodePtr;
    typedef NodePtr* NodePtrPtr;
    typedef NodePtr& NodePtrRef;
    
private:
    typedef unordered_map<string, NodePtrRef> nodeMap;
    typedef tuple<NodePtrPtr, size_t, size_t*> nodeArrayElement;
    typedef unordered_map<string, nodeArrayElement> nodeArrayMap;
    typedef unordered_map<string, Control::Handler> controlHandlerMap;
    typedef unordered_map<string, Value&> valueMap;
    typedef unordered_map<string, SEL_MenuHandler> menuHandlerMap;
    typedef unordered_map<string, SEL_CallFuncN> callFuncMap;
    
    controlHandlerMap   _mapControlHandlers;
    valueMap            _mapProps;
    menuHandlerMap      _mapMenuHandlers;
    callFuncMap         _mapCallFuncs;
    nodeMap             _mapMemberVars;
    nodeArrayMap        _mapNodeArray;

    virtual void onNodeLoaded(Node * pNode, NodeLoader * pNodeLoader);
    virtual bool onAssignCCBMemberVariable(Ref* target, const char* memberVariableName, Node* node);
    virtual bool onAssignCCBCustomProperty(Ref* target, const char* memberVariableName, const Value& value);
    
    virtual SEL_MenuHandler onResolveCCBCCMenuItemSelector(Ref * pTarget, const char* pSelectorName);
    virtual SEL_CallFuncN onResolveCCBCCCallFuncSelector(Ref * pTarget, const char* pSelectorName);
    virtual Control::Handler onResolveCCBCCControlSelector(Ref * pTarget, const char* pSelectorName);
    
public:
    string      _file;
    string      _rootPath;
    Size        _parentSize;
    Ref*        _owner;

    CCBILoader() {_parentSize = Size::ZERO; _owner = nullptr;}
    void reset()
    {
        _mapControlHandlers.clear();
        _mapProps.clear();
        _mapMenuHandlers.clear();
        _mapCallFuncs.clear();
        _mapMemberVars.clear();
        _file.clear();
        _owner = nullptr;
        _parentSize = Size::ZERO;
    }
    
    Node* loadFile(const char* ccbiFileName, Ref* owner, Size parentSize = Size::ZERO)
    {
        _file = ccbiFileName;
        _owner = owner;
        _parentSize = parentSize;
        return this->load();
    }
    
    Node* load();
    
    void mapMember(const char* name, NodePtrRef node)
    {
        node = nullptr;
        _mapMemberVars.insert(nodeMap::value_type(name, node));
    }

    void mapMemberArray(const char* name, NodePtrPtr nodeArray, size_t capacity, size_t& numNodes)
    {
        memset(nodeArray, 0, capacity * sizeof(NodePtr));
        numNodes = 0;
        auto elem = make_tuple(nodeArray, capacity, &numNodes);
        _mapNodeArray.insert(nodeArrayMap::value_type(name, elem));
    }
    
    void mapControlHandler(const char* name, Control::Handler handler)
    {
        _mapControlHandlers.insert(controlHandlerMap::value_type(name, handler));
    }
    
    void mapProperty(const char* name, Value& value)
    {
        _mapProps.insert(valueMap::value_type(name, value));
    }
    
    void mapMenuHandler(const char* name, SEL_MenuHandler handler)
    {
        _mapMenuHandlers.insert(menuHandlerMap::value_type(name, handler));
    }
    
    void mapCallFunc(const char* name, SEL_CallFuncN pfn)
    {
        _mapCallFuncs.insert(callFuncMap::value_type(name, pfn));
    }
};

// Helper macros

// Loader

#define BEGIN_CCBI_LOADER(file) \
auto ccbiLoader = new CCBILoader;\
ccbiLoader->_file = file;\
ccbiLoader->_owner = this;

#define BEGIN_CCBI_LOADER2(file, owner) \
auto ccbiLoader = new CCBILoader;\
ccbiLoader->_file = file;\
ccbiLoader->_owner = this;

#define BEGIN_CCBI_LOADER3(file, owner, parentSize) \
auto ccbiLoader = new CCBILoader;\
ccbiLoader->_file = file;\
ccbiLoader->_owner = this;\
ccbiLoader->_parentSize = parentSize;

#define CCBI_ROOT_PATH(rootPath) ccbiLoader->_rootPath = rootPath;
#define CCBI_OWER(owner) ccbiLoader->_owner = owner;
#define CCBI_PARENT_SIZE(size) ccbiLoader->_parentSize = size;

#define END_CCBI_LOADER \
auto ccbiLoaderNode = ccbiLoader->load(); delete ccbiLoader;\
ccbiLoaderNode; // remove compiler warning if this var is not used

#define END_CCBI_LOADER2(nodeVarName) \
auto nodeVarName = ccbiLoader->load(); delete ccbiLoader;


// member variable
#define CCBI_MEMBER(var) \
ccbiLoader->mapMember(#var, (CCBILoader::NodePtrRef)var);

#define CCBI_MEMBER2(name, var) \
ccbiLoader->mapMember(name, (CCBILoader::NodePtrRef)var);

// member variable array
#define CCBI_MEMBER_ARRAY(var, capacity, numNodes) \
ccbiLoader->mapMemberArray(#var, (CCBILoader::NodePtrPtr)var, capacity, numNodes);

#define CCBI_MEMBER_ARRAY2(name, var, capacity, numNodes) \
ccbiLoader->mapMemberArray(name, (CCBILoader::NodePtrPtr)var, capacity, numNodes);


// property
#define CCBI_PROPERTY(var) \
ccbiLoader->mapProperty(#var, var);

#define CCBI_PROPERTY2(name, var) \
ccbiLoader->mapProperty(name, var);


// control handler
#define CCBI_CONTROL_HANDLER(name, handler) \
ccbiLoader->mapControlHandler(name, (Control::Handler)&handler);


// menu handler
#define CCBI_MENU_HANDLER(name, handler) \
ccbiLoader->mapMenuHandler(name, (SEL_MenuHandler)&handler);


// call func
#define CCBI_FUNC(name, fn) \
ccbiLoader->mapCallFunc(name, (SEL_CallFuncN)&fn);


#endif
